/* eslint-disable no-param-reassign */
import { RuntimeError } from '@ibiz-template/core';
import {
  ControlController,
  getPanelItemProvider,
  IControlProvider,
  IPanelController,
  IPanelEvent,
  IPanelItemContainerController,
  IPanelItemController,
  IPanelItemProvider,
  IPanelState,
  PanelNotifyState,
} from '@ibiz-template/runtime';
import {
  IPanel,
  IPanelContainer,
  IPanelItem,
  IPanelTabPanel,
} from '@ibiz/model-core';

/**
 * 面板部件控制器
 *
 * @author lxm
 * @date 2022-09-08 20:09:55
 * @export
 * @class PanelController
 * @extends {ControlController<PanelModel>}
 */
export class PanelController<
    T extends IPanel = IPanel,
    S extends IPanelState = IPanelState,
    E extends IPanelEvent = IPanelEvent,
  >
  extends ControlController<T, S, E>
  implements IPanelController<T, S, E>
{
  /**
   * 所有面板成员的控制器
   *
   * @author lxm
   * @date 2022-08-24 20:08:07
   * @type {{ [key: string]: IPanelItemController }}
   */
  panelItems: { [key: string]: IPanelItemController } = {};

  /**
   * 所有面板成员的适配器
   *
   * @author lxm
   * @date 2022-08-24 20:08:07
   * @type {{ [key: string]: IPanelItemProvider  | IControlProvider}}
   */
  providers: { [key: string]: IPanelItemProvider | IControlProvider } = {};

  /**
   * 外部输入数据
   *
   * @author lxm
   * @date 2023-02-09 03:16:52
   * @type {IData}
   * @memberof PanelController
   */
  inputData: IData | undefined;

  /**
   * 面板数据
   *
   * @author lxm
   * @date 2023-02-10 07:21:09
   * @readonly
   * @memberof PanelController
   */
  get data(): IData {
    return this.state.data;
  }

  protected initState(): void {
    super.initState();
    this.state.data = {};
  }

  setInputData(data: IData | undefined): void {
    this.inputData = data;
  }

  protected async onCreated(): Promise<void> {
    await super.onCreated();
    await this.initPanelItemControllers();
  }

  protected async onMounted(): Promise<void> {
    this.load();
  }

  /**
   * 初始化面板成员控制器
   *
   * @author lxm
   * @date 2022-08-24 21:08:48
   * @protected
   */
  protected async initPanelItemControllers(
    panelItems: IPanelItem[] | undefined = this.model.rootPanelItems,
    panel: PanelController = this,
    parent: IPanelItemContainerController | undefined = undefined,
  ): Promise<void> {
    if (!panelItems) {
      return;
    }
    await Promise.all(
      panelItems.map(async panelItem => {
        // 生成面板成员控制器
        const panelItemProvider = await getPanelItemProvider(panelItem);
        if (!panelItemProvider) {
          return;
        }
        panel.providers[panelItem.id!] = panelItemProvider;
        const panelItemController = await panelItemProvider.createController(
          panelItem,
          panel,
          parent,
        );
        panel.panelItems[panelItem.id!] = panelItemController;
        // 有子成员的生成子控制器
        if ((panelItem as IPanelContainer).panelItems?.length) {
          await this.initPanelItemControllers(
            (panelItem as IPanelContainer).panelItems,
            panel,
            panelItemController as IPanelItemContainerController,
          );
        }
        if ((panelItem as IPanelTabPanel).panelTabPages?.length) {
          await this.initPanelItemControllers(
            (panelItem as IPanelTabPanel).panelTabPages,
            panel,
            panelItemController as IPanelItemContainerController,
          );
        }
      }),
    );
  }

  /**
   * 部件加载，获取数据，并执行一系列后续初始化逻辑
   *
   * @author lxm
   * @date 2023-02-10 01:46:24
   * @memberof PanelController
   */
  async load(): Promise<void> {
    // 准备面板数据
    const data = await this.prepareData();
    if (!data) {
      throw new RuntimeError('未获取到面板数据');
    }
    // 转换数据，处理原始数据和面板项的映射。
    const panelData = this.convertData(data);

    this.state.data = panelData;
    this.panelStateNotify(PanelNotifyState.LOAD);
  }

  /**
   * 根据获取模式准备原始数据
   *
   * @author lxm
   * @date 2023-02-10 02:04:39
   * @returns {*}  {(Promise<IData | undefined>)}
   * @memberof PanelController
   */
  async prepareData(): Promise<IData | undefined> {
    let data;
    // 根据数据获取模式获取数据
    switch (this.model.dataMode) {
      // 未传入时获取
      case 1:
        if (this.inputData) {
          data = this.inputData;
        }
        break;
      // 不获取（使用传入数据）
      default:
        data = this.inputData || {};
    }
    return data;
  }

  /**
   * 转换原始数据，映射面板属性
   *
   * @author lxm
   * @date 2023-02-10 02:10:41
   * @param {IData} data
   * @returns {*}  {IData}
   * @memberof PanelController
   */
  convertData(data: IData): IData {
    // const map: IData = {};
    // // 绑定数据项和name不一致的映射到map里
    // this.model.panelFields.forEach(field => {
    //   if (field.id !== field.dataFieldName) {
    //     map[field.id] = field.dataFieldName;
    //   }
    // });

    // return new Proxy(data, {
    //   get(target, p, receiver) {
    //     if (Reflect.ownKeys(map).includes(p)) {
    //       return Reflect.get(target, map[p], receiver);
    //     }
    //     return Reflect.get(target, p, receiver);
    //   },
    //   set(target, p, value, receiver) {
    //     Reflect.set(target, p, value, receiver);
    //     return true;
    //   },
    // });
    return data;
  }

  /**
   * 通知所有面板成员面板操作过程中的数据变更
   *
   * @author lxm
   * @date 2022-09-20 18:09:40
   * @param {string[]} names
   */
  dataChangeNotify(names: string[]): void {
    Object.values(this.panelItems).forEach(panelItem => {
      panelItem.dataChangeNotify(names);
    });
  }

  /**
   * 面板状态变更通知
   *
   * @author lxm
   * @date 2022-09-20 18:09:07
   */
  panelStateNotify(state: PanelNotifyState): void {
    Object.values(this.panelItems).forEach(panelItem => {
      panelItem.panelStateNotify(state);
    });
  }

  /**
   * 设置面板数据的值
   *
   * @param {string} name 要设置的数据的属性名称
   * @param {unknown} value 要设置的值
   */
  async setDataValue(name: string, value: unknown): Promise<void> {
    if (
      Object.prototype.hasOwnProperty.call(this.state.data, name) &&
      this.state.data[name] === value
    ) {
      // *`面板里没有属性${name}或者${name}的值未发生改变`
      return;
    }

    // 改变值
    this.state.data[name] = value;
    this.dataChangeNotify([name]);
  }
}
